﻿Imports System.IO
Module Scripts
    Public Enum ScriptType
        startup
        dormant
        continuous
        [static]
        stub
    End Enum
    Public Enum ScriptValueType As Short
        unparsed
        special_form
        function_name
        passthrough
        void
        [boolean]
        real
        [short]
        [long]
        [string]
        script
        trigger_volume
        cutscene_flag
        cutscene_camera_point
        cutscene_title
        cutscene_recording
        device_group
        ai
        ai_command_list
        starting_profile
        conversation
        navpoint
        hud_message
        object_list
        sound
        effect
        damage
        looping_sound
        animation_graph
        actor_variant
        damage_effect
        object_definition
        game_difficulty
        team
        ai_default_state
        actor_type
        hud_corner
        [object]
        unit
        vehicle
        weapon
        device
        scenery
        object_name
        unit_name
        vehicle_name
        weapon_name
        device_name
        scenery_name
    End Enum
    Public Enum GameDifficulty As Short
        easy
        normal
        hard
        impossible
    End Enum
    Public Structure syntaxHeader
        Dim Name As String
        Dim maxObjects As Short
        Dim Size As Short
        Dim Unk1 As Integer
        Dim Data As Byte()
        Dim Unk2 As Short
        Dim maxNum As Short
        Dim NextObjectIndex As Short
        Dim NextObjectID As Short
        Dim FirstObject As UInteger
        Sub New(ByVal BR As BinaryReader)
            Name = System.Text.ASCIIEncoding.ASCII.GetString(BR.ReadBytes(32)).Trim(Chr(0))
            maxObjects = BR.ReadInt16
            Size = BR.ReadInt16
            Unk1 = BR.ReadInt32
            Data = BR.ReadBytes(4)
            Unk2 = BR.ReadInt16
            maxNum = BR.ReadInt16
            NextObjectIndex = BR.ReadInt16
            NextObjectID = BR.ReadInt16
            FirstObject = BR.ReadUInt32
        End Sub
    End Structure

    Public Structure syntaxStructure
        Dim ExpressionIndex As Short  '//unique syntax ID
        Dim Identity As Short  ' //the script commands unique engine identifier
        Dim ValueType As ScriptValueType  ' //The scripts value type
        Dim ExpressionType As Short  ' //The type of syntax this is  ex. 13 = global
        Dim Sibling As Integer  ' //0's ?
        Dim StringPntr As Integer  ' // how far into script table text for this syntax is, this is not a real pointer but a value you add to start of StringTable to find were it is
        Dim Value As Byte()  ' //this is the Syntax value, depending it may be of 2 or 4 bytes and different data uses
        Sub New(ByVal br As BinaryReader)
            ExpressionIndex = br.ReadInt16
            Identity = br.ReadInt16
            ValueType = br.ReadInt16
            ExpressionType = br.ReadInt16
            Sibling = br.ReadInt32
            StringPntr = br.ReadInt32
            Value = br.ReadBytes(4)
            Array.Reverse(Value)
        End Sub
    End Structure
    Public Structure scriptStructure
        Dim Name As String
        Dim ScriptType As Short
        Dim ReturnType As Short
        Dim NodeIndex As Short
        Dim NodePointer As Short
    End Structure


    Dim ScriptHeader As syntaxHeader
    Dim ScriptChunks() As syntaxStructure
    Public Scripts() As scriptStructure
    Dim Globals() As scriptStructure
    Dim stringReader As BinaryReader
    Sub LoadScripts(ByVal scnrTag As Magic.Core.Tag)

        Dim syntaxData As Byte() = scnrTag("script syntax data").data
        Dim stringData As Byte() = scnrTag("script string data").data
        Dim syntaxReader As New Magic.Core.EndianReader(New MemoryStream(syntaxData), Magic.Core.ByteOrder.BigEndian)
        stringReader = New BinaryReader(New MemoryStream(stringData))

        ScriptHeader = New syntaxHeader(syntaxReader)
        ReDim ScriptChunks(ScriptHeader.maxNum - 1)

        For x As Integer = 0 To ScriptHeader.maxNum - 1
            ScriptChunks(x) = New syntaxStructure(syntaxReader)
        Next

        Dim scriptData As Array = scnrTag("scripts")
        Dim scriptCount As Integer = scriptData.GetLength(0)
        ReDim Scripts(scriptCount - 1)

        For x As Integer = 0 To scriptCount - 1
            Scripts(x) = New scriptStructure
            Scripts(x).Name = CStr(scriptData.GetValue(x, 0)).Trim(Chr(0))
            Scripts(x).ScriptType = scriptData.GetValue(x, 1)
            Scripts(x).ReturnType = scriptData.GetValue(x, 2)
            Scripts(x).NodeIndex = BitConverter.ToInt16(BitConverter.GetBytes(scriptData.GetValue(x, 3)), 0)
            Scripts(x).NodePointer = BitConverter.ToInt16(BitConverter.GetBytes(scriptData.GetValue(x, 3)), 2)
        Next

        Dim globalData As Array = scnrTag("globals")
        Dim globalCount As Integer = globalData.GetLength(0)
        ReDim Globals(globalCount - 1)

        For x As Integer = 0 To globalCount - 1
            Globals(x) = New scriptStructure
            Globals(x).Name = CStr(globalData.GetValue(x, 0)).Trim(Chr(0))
            'Globals(x).ScriptType = globalData.GetValue(x, 1)
            Globals(x).ReturnType = globalData.GetValue(x, 1)
            Globals(x).NodeIndex = BitConverter.ToInt16(BitConverter.GetBytes(globalData.GetValue(x, 4)), 0)
            Globals(x).NodePointer = BitConverter.ToInt16(BitConverter.GetBytes(globalData.GetValue(x, 4)), 2)
        Next

        For x As Integer = 0 To scriptCount - 1
            ParseScript(Scripts(x), scnrTag)
        Next
        For x As Integer = 0 To globalCount - 1
            ParseScript(Globals(x), scnrTag)
        Next
    End Sub
    Sub SwapScripts(ByRef scnrTag As Magic.Core.Tag)

        Dim syntaxData As Byte() = scnrTag("script syntax data").data
        Dim syntaxReader As New BinaryReader(New MemoryStream(syntaxData))
        Dim reverseStream As New MemoryStream(syntaxData)
        Dim reverseWriter As New Magic.Core.EndianWriter(reverseStream, Magic.Core.ByteOrder.BigEndian)

        reverseWriter.Write(syntaxReader.ReadBytes(32))
        reverseWriter.Write(syntaxReader.ReadInt16)
        reverseWriter.Write(syntaxReader.ReadInt16)
        reverseWriter.Write(syntaxReader.ReadBytes(4)) 'BYTES??
        reverseWriter.Write(syntaxReader.ReadInt32)
        reverseWriter.Write(syntaxReader.ReadInt16)
        Dim maxNum As Int16 = syntaxReader.ReadInt16
        reverseWriter.Write(maxNum)
        reverseWriter.Write(syntaxReader.ReadInt16)
        reverseWriter.Write(syntaxReader.ReadInt16)
        reverseWriter.Write(syntaxReader.ReadUInt32)

        For x As Integer = 0 To maxNum - 1
            'ScriptChunks(x) = New syntaxStructure(syntaxReader)
            reverseWriter.Write(syntaxReader.ReadInt16)
            reverseWriter.Write(syntaxReader.ReadInt16)
            reverseWriter.Write(syntaxReader.ReadInt16)
            reverseWriter.Write(syntaxReader.ReadInt16)
            reverseWriter.Write(syntaxReader.ReadInt32)
            reverseWriter.Write(syntaxReader.ReadInt32)
            reverseWriter.Write(syntaxReader.ReadInt32)
            'reverseWriter.Write(syntaxReader.ReadBytes(4))
        Next
        reverseStream.Position = 0
        'scnrTag("script syntax data").variable = 0
        scnrTag("script syntax data").data = reverseStream.ToArray
        'scnrTag("script string data").variable = 0

        'scnrTag("script string data").data = New Byte() {} '
    End Sub
    Function ParseScript(ByVal nScript As scriptStructure, ByRef scnrTag As Magic.Core.Tag) As String
        ' (script type name value)
        Dim root As Integer = 0
        Dim scriptText As String
        scriptText = "(script " & System.Enum.GetName(GetType(ScriptType), nScript.ScriptType) & " " & nScript.Name & " " & Environment.NewLine
        ParseChildren(scriptText, nScript.NodeIndex, nScript.NodePointer, scnrTag, root)
        scriptText &= ")"
        'Console.WriteLine(scriptText)
        Return scriptText
    End Function
    Sub ParseChildren(ByRef scriptText As String, ByVal sIndex As Short, ByVal sExp As Short, ByRef scnrTag As Magic.Core.Tag, ByVal root As Integer)
        Dim Script As syntaxStructure = ScriptChunks(sIndex)
        If (Script.ExpressionIndex <> sExp) Then
            scriptText &= "Error, index mismatch."
        End If
        Select Case (Script.ExpressionType)

            Case 8 '// xbe function call
                'scriptText &= Environment.NewLine & "{" & root & "}" & "("
                scriptText &= IIf(root = 1, Environment.NewLine, "") & "("
                ParseChildren(scriptText, BitConverter.ToInt16(Script.Value, 0), BitConverter.ToInt16(Script.Value, 2), scnrTag, root + 1)
                scriptText &= ")"
            Case 9 '// statement
                If magicMap.Library.IndexByID(BitConverter.ToUInt32(Script.Value, 0)) > -1 Then
                    Dim tagName As String = magicMap.Library(magicMap.Library.IndexByID(BitConverter.ToUInt32(Script.Value, 0))).Name
                    Dim tagExtension As String = magicMap.Library(magicMap.Library.IndexByID(BitConverter.ToUInt32(Script.Value, 0))).Xpe.Extension

                    Debug.WriteLine("Tag: {0}.{1}, Ident: {2}, ValueType: {3}", tagName, tagExtension, "0x" & BitConverter.ToUInt32(Script.Value, 0).ToString("X8"), Script.ValueType)
                End If

                Select Case (Script.ValueType)

                    Case ScriptValueType.function_name
                        stringReader.BaseStream.Position = Script.StringPntr
                        Dim tChr As Byte = 0
                        Dim Done As Boolean = False
                        Do
                            tChr = stringReader.ReadByte
                            If tChr <> 0 Then
                                scriptText &= Convert.ToChar(tChr).ToString
                            Else
                                Done = True
                            End If
                        Loop While Not Done

                    Case ScriptValueType.boolean
                        If BitConverter.ToBoolean(Script.Value, 0) Then
                            scriptText &= "true"
                        Else
                            scriptText &= "false"
                        End If

                    Case ScriptValueType.real
                        scriptText &= BitConverter.ToSingle(Script.Value, 0)

                    Case ScriptValueType.short
                        scriptText &= BitConverter.ToInt16(Script.Value, 0)

                    Case ScriptValueType.long
                        scriptText &= BitConverter.ToInt32(Script.Value, 0)

                    Case ScriptValueType.string
                        scriptText &= """"
                        stringReader.BaseStream.Position = Script.StringPntr
                        Dim tChr As Byte = 0
                        Dim Done As Boolean = False
                        Do
                            tChr = stringReader.ReadByte
                            If tChr <> 0 Then
                                scriptText &= Convert.ToChar(tChr).ToString
                            Else
                                Done = True
                            End If
                        Loop While Not Done
                        scriptText &= """"
                    Case ScriptValueType.looping_sound, ScriptValueType.sound, ScriptValueType.effect, ScriptValueType.object_definition, ScriptValueType.animation_graph, ScriptValueType.actor_variant, ScriptValueType.damage
                        scriptText &= """"
                        stringReader.BaseStream.Position = Script.StringPntr
                        Dim tChr As Byte = 0
                        Dim Done As Boolean = False
                        Do
                            tChr = stringReader.ReadByte
                            If tChr <> 0 Then
                                scriptText &= Convert.ToChar(tChr).ToString
                            Else
                                Done = True
                            End If
                        Loop While Not Done
                        scriptText &= """"

                    Case ScriptValueType.script
                        scriptText &= Scripts(BitConverter.ToInt16(Script.Value, 0)).Name

                        'Case 
                        '   Dim tID As UInt32 = BitConverter.ToUInt32(Script.Value, 0)
                        '  Dim mID As Integer = magicMap.Library.IndexByID(tID)
                        ' scriptText &= """" & magicMap.Library.GetName(mID) & """"
                        'Dim ttID As Integer = magicMap.Library.IndexByName("levels\a10\music\a10_01", "lsnd")
                        'Dim re As Integer = 0

                    Case ScriptValueType.object, ScriptValueType.object_name, ScriptValueType.object_list, ScriptValueType.unit, ScriptValueType.unit_name, ScriptValueType.vehicle, ScriptValueType.vehicle_name
                        Dim oIndex As Short = BitConverter.ToInt16(Script.Value, 0)
                        If oIndex > -1 Then
                            scriptText &= CStr(CType(scnrTag("object names"), Array).GetValue(oIndex, 0)).Trim(Chr(0))
                        Else
                            scriptText &= "none"
                        End If

                    Case ScriptValueType.cutscene_flag
                        Dim oIndex As Short = BitConverter.ToInt32(Script.Value, 0)
                        If oIndex > -1 Then
                            scriptText &= CStr(CType(scnrTag("cutscene flags"), Array).GetValue(oIndex, 1)).Trim(Chr(0))
                        Else
                            scriptText &= "none"
                        End If

                    Case ScriptValueType.cutscene_camera_point
                        Dim oIndex As Short = BitConverter.ToInt32(Script.Value, 0)
                        If oIndex > -1 Then
                            scriptText &= CStr(CType(scnrTag("cutscene camera points"), Array).GetValue(oIndex, 1)).Trim(Chr(0))
                        Else
                            scriptText &= "none"
                        End If

                    Case ScriptValueType.cutscene_title
                        Dim oIndex As Short = BitConverter.ToInt32(Script.Value, 0)
                        If oIndex > -1 Then
                            Dim bytestr As Byte() = System.Text.ASCIIEncoding.ASCII.GetBytes(CStr(CType(scnrTag("cutscene titles"), Array).GetValue(oIndex, 1)).Trim(Chr(0)))
                            Dim bytestr2 As Byte() = System.Text.ASCIIEncoding.ASCII.GetBytes(CStr(CType(scnrTag("cutscene titles"), Array).GetValue(oIndex, 1)))
                            scriptText &= CStr(CType(scnrTag("cutscene titles"), Array).GetValue(oIndex, 1)).Trim(Chr(0)) & ")"
                        Else
                            scriptText &= "none"
                        End If

                    Case ScriptValueType.cutscene_recording
                        Dim oIndex As Short = BitConverter.ToInt32(Script.Value, 0)
                        If oIndex > -1 Then
                            scriptText &= CStr(CType(scnrTag("recorded animations"), Array).GetValue(oIndex, 0)).Trim(Chr(0))
                        Else
                            scriptText &= "none"
                        End If

                    Case ScriptValueType.trigger_volume
                        Dim oIndex As Short = BitConverter.ToInt32(Script.Value, 0)
                        If oIndex > -1 Then
                            scriptText &= CStr(CType(scnrTag("trigger volumes"), Array).GetValue(oIndex, 1)).Trim(Chr(0))
                        Else
                            scriptText &= "none"
                        End If

                    Case ScriptValueType.ai_command_list
                        Dim oIndex As Short = BitConverter.ToInt32(Script.Value, 0)
                        If oIndex > -1 Then
                            scriptText &= CStr(CType(scnrTag("command lists"), Array).GetValue(oIndex, 0)).Trim(Chr(0))
                        Else
                            scriptText &= "none"
                        End If

                    Case ScriptValueType.game_difficulty
                        Dim dIndex As Short = BitConverter.ToInt16(Script.Value, 0)
                        scriptText &= System.Enum.GetName(GetType(GameDifficulty), dIndex)

                    Case ScriptValueType.hud_message
                        Dim oIndex As Short = BitConverter.ToInt32(Script.Value, 0)
                        If oIndex > -1 Then
                            Dim tIndex As Integer = scnrTag("hud messages")
                            Dim HUD As Magic.Core.Tag = magicMap.Library(tIndex)
                            scriptText &= CStr(CType(HUD("messages"), Array).GetValue(oIndex, 0)).Trim(Chr(0))
                        Else
                            scriptText &= "none"
                        End If
                    Case ScriptValueType.ai
                        Dim Decision As Short = BitConverter.ToInt16(Script.Value, 2) >> 30
                        Dim Squad As Short = BitConverter.ToInt16(Script.Value, 2) And &H3FFF
                        Dim Encounter As Short = BitConverter.ToInt16(Script.Value, 0)
                        'Dim trueValue
                        Select Case Math.Abs(Decision)
                            Case 0 'Just an encounter
                                scriptText &= CStr(CType(scnrTag("encounters"), Array).GetValue(Encounter, 0)).Trim(Chr(0))
                            Case 1 'Platoon?
                                scriptText &= CStr(CType(scnrTag("encounters"), Array).GetValue(Encounter, 0)).Trim(Chr(0)) & "/"
                                scriptText &= CStr(CType(CType(scnrTag("encounters"), Array).GetValue(Encounter, 9), Array).GetValue(Squad, 0)).Trim(Chr(0))
                            Case 2 'Squad?
                                scriptText &= CStr(CType(scnrTag("encounters"), Array).GetValue(Encounter, 0)).Trim(Chr(0)) & "/"
                                scriptText &= CStr(CType(CType(scnrTag("encounters"), Array).GetValue(Encounter, 8), Array).GetValue(Squad, 0)).Trim(Chr(0))
                        End Select
                    Case ScriptValueType.void

                    Case Else
                        scriptText &= "[" & System.Enum.GetName(GetType(ScriptValueType), Script.ValueType) & " = " & BitConverter.ToInt32(Script.Value, 0).ToString("x") & "]"
                        'Console.WriteLine("[" & System.Enum.GetName(GetType(ScriptValueType), Script.ValueType) & " = " & BitConverter.ToInt32(Script.Value, 0).ToString("x") & "]")

                End Select
            Case 10 '// script call
                scriptText &= IIf(root = 1, Environment.NewLine, "") & "("
                ParseChildren(scriptText, BitConverter.ToInt16(Script.Value, 0), BitConverter.ToInt16(Script.Value, 2), scnrTag, root + 1)
                scriptText &= ")" '& IIf(root < 3, Environment.NewLine, "")
            Case 13 '// global
                Dim gIndex As Short = BitConverter.ToInt16(Script.Value, 0)
                If gIndex > -1 AndAlso gIndex < Globals.Count Then
                    scriptText &= Globals(gIndex).Name
                Else
                    scriptText &= "GLOBAL= " & gIndex
                End If
            Case Else
                scriptText &= "ExpressionType= " & Script.ExpressionType

        End Select
        If (Script.Sibling <> &HFFFFFFFF) Then
            scriptText &= " "
            ParseChildren(scriptText, BitConverter.ToInt16(BitConverter.GetBytes(Script.Sibling), 0), BitConverter.ToInt16(BitConverter.GetBytes(Script.Sibling), 2), scnrTag, root)
        Else
            ' scriptText &= root & Environment.NewLine
        End If
    End Sub
End Module
